/////////////////////////////////////////////////////////////////////////////
// Copyright(c) 2012-2015 purang All Rights Reserved
// Name:        rw_lock_generic.cc
// Purpose:     
// Author:      syy
// Modified by: 
// Created:     2014/1/3 15:40
// RCS-ID:      
// Licence:     
/////////////////////////////////////////////////////////////////////////////

#include "rw_lock_generic.h"

#include "condition_variable_wrapper.h"
#include "critical_section_wrapper.h"

namespace qb
{

	RWLockGeneric::RWLockGeneric()
		: readers_active_(0),
		writer_active_(false),
		readers_waiting_(0),
		writers_waiting_(0)
	{
		critical_section_ = CriticalSectionWrapper::CreateCriticalSection();
		read_condition_ = ConditionVariableWrapper::CreateConditionVariable();
		write_condition_ = ConditionVariableWrapper::CreateConditionVariable();
	}

	RWLockGeneric::~RWLockGeneric()
	{
		delete write_condition_;
		delete read_condition_;
		delete critical_section_;
	}

	void RWLockGeneric::AcquireLockExclusive()
	{
		CriticalSectionScoped cs(critical_section_);
		if (writer_active_ || readers_active_ > 0)
		{
			++writers_waiting_;
			while (writer_active_ || readers_active_ > 0)
			{
				write_condition_->SleepCS(*critical_section_);
			}
			--writers_waiting_;
		}
		writer_active_ = true;
	}

	void RWLockGeneric::ReleaseLockExclusive()
	{
		CriticalSectionScoped cs(critical_section_);
		writer_active_ = false;
		if (writers_waiting_ > 0)
		{
			write_condition_->Wake();
		} else if (readers_waiting_ > 0)
		{
			read_condition_->WakeAll();
		}
	}

	void RWLockGeneric::AcquireLockShared()
	{
		CriticalSectionScoped cs(critical_section_);
		if (writer_active_ || writers_waiting_ > 0)
		{
			++readers_waiting_;

			while (writer_active_ || writers_waiting_ > 0)
			{
				read_condition_->SleepCS(*critical_section_);
			}
			--readers_waiting_;
		}
		++readers_active_;
	}

	void RWLockGeneric::ReleaseLockShared()
	{
		CriticalSectionScoped cs(critical_section_);
		--readers_active_;
		if (readers_active_ == 0 && writers_waiting_ > 0)
		{
			write_condition_->Wake();
		}
	}

}  // namespace qb
